Skip to content

Conversation

@quinnj
Copy link
Member

@quinnj quinnj commented Jan 14, 2026

Summary

This is a complete rewrite of JSONSchema.jl that transforms it from a pure validation library to a schema generation and validation library.

New Features

  • schema(T): Generate JSON Schema from Julia struct definitions
  • Schema{T}: Type-parameterized schema for type-safe validation
  • StructUtils integration for field-level validation tags (min/max, patterns, formats, etc.)
  • $ref support for schema deduplication and recursive types
  • ValidationResult struct with detailed error messages
  • Comprehensive documentation with migration guide

Breaking Changes

  • validate() now returns ValidationResult instead of nothing/SingleIssue
  • JSON.schema/validate/isvalid convenience methods removed (use JSONSchema.* directly)
  • parent_dir kwarg removed from Schema constructor
  • Minimum Julia version bumped to 1.10

Backwards Compatibility Layer

Most v1.x code will continue to work:

  • schema.data field access (maps to schema.spec)
  • Schema(true)/Schema(false) boolean schemas
  • validate(data, schema) and isvalid(data, schema) inverse argument order
  • required validation without properties
  • diagnose() function (deprecated but functional)
  • SingleIssue type alias

Other Changes

  • Removed JSON3 extension (no longer needed)
  • Updated CI to modern GitHub Actions
  • Added comprehensive documentation site
  • Version bump to 2.0.0

Test plan

  • All existing tests pass
  • JSON Schema Test Suite (draft4, draft6, draft7) passes
  • Backwards compatibility tests pass
  • CI passes on all platforms

🤖 Generated with Claude Code

This is a complete rewrite of JSONSchema.jl that transforms it from a
pure validation library to a schema generation and validation library.

## New Features
- `schema(T)`: Generate JSON Schema from Julia struct definitions
- `Schema{T}`: Type-parameterized schema for type-safe validation
- StructUtils integration for field-level validation tags
- `$ref` support for schema deduplication and recursive types
- `ValidationResult` struct with detailed error messages

## Breaking Changes
- `validate()` now returns `ValidationResult` instead of `nothing`/`SingleIssue`
- `JSON.schema/validate/isvalid` convenience methods removed
- `parent_dir` kwarg removed from `Schema` constructor
- See docs/src/migration.md for full migration guide

## Backwards Compatibility
- `schema.data` field access (maps to `schema.spec`)
- `Schema(true)`/`Schema(false)` boolean schemas
- `validate(data, schema)` inverse argument order
- `isvalid(data, schema)` inverse argument order
- `required` validation without `properties`
- `diagnose()` function (deprecated)
- `SingleIssue` type alias

## Other Changes
- Removed JSON3 extension
- Updated CI to modern GitHub Actions
- Added comprehensive documentation
- Bumped minimum Julia version to 1.10
- Version bump to 2.0.0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@quinnj quinnj merged commit 241316b into master Jan 14, 2026
9 checks passed
@odow
Copy link
Collaborator

odow commented Jan 14, 2026

I don't necessarily object, but merging in 7 minutes without review is not good practice.

@odow
Copy link
Collaborator

odow commented Jan 14, 2026

What does "Generated with Claude Code" mean. To what extent?

Why was this rewrite necessary? Who is the use-case for?

This needed much more discussion before merging.

@quinnj
Copy link
Member Author

quinnj commented Jan 14, 2026

Sorry, yeah, that was hasty of me. Too eager to get a release out for other downstream changes/dependencies. I was partly wondering if anyone would want to review such a big rewrite, but I should have at least let it stay open.

I'm using claude mostly to help with actual package maintenance tasks:

  • investigating issues
  • reviewing PRs
  • running tests, making a branch, commiting a fix, opening a PR, etc.

The rewrite updates the spec version teh package supports (draft6 -> draft7), and includes new "schema generation" functionality that I need in other projects. It integrates with the StructUtils.jl package to utilize the "field tag" functionality where you can specify, per field, json schema properties/constraints/etc.

I tried to do a fairly comprehensive review of the latest 1.5 release and add in a "compat.jl" layer ot make the 2.0 release as minimally breaking as possible, along with a "migration guide" for the few remaining minor breakign pieces.

Schema{T}
A typed JSON Schema for type `T`. Contains the schema specification and can be used
for validation via `JSON.isvalid`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON.isvalid doesn't exist. Did you mean JSONSchema.isvalid? Otherwise, why aren't we overloading Base.isvalid like we were previously? This break is a breaking change for JuMP.

```julia
schema = JSON.schema(User)
instance = User("alice", "alice@example.com", 25)
is_valid = JSON.isvalid(schema, instance)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code isn't self-contained. What is User? Same issue with JSON/JSONSchema

old_path = match(r"^(.*/).*$", uri.path)
if old_path === nothing
els[:path] = id2.path
# JSON Schema generation and validation from Julia types
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly prefer we retain the copyright headers at the top of files

# Context for tracking type definitions during schema generation with $ref support
mutable struct SchemaContext
# Map from Type to definition name
type_names::Dict{Type, String}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason for removing JuliaFormatter?


on:
push:
branches: [main, master]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main branch doesn't exist

],
)

deploydocs(repo = "github.com/JuliaServices/JSONSchema.jl.git", push_preview = true)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is JuliaServices? Surely this needs to be JuliaIO?


## Quick Start

```julia
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong preference that all code blocks in the documentation need to be @repl otherwise it's too easy for there to be a mistake.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'm not familiar with @repl; I'll study up and change.

### 1. `validate()` Return Type

**This is the main breaking change.** The `validate` function now always returns
a `ValidationResult` struct instead of `nothing` on success.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the motivation for this?

**Migration:** Replace `validate(...) === nothing` with `validate(...).is_valid`
or use `isvalid(schema, data)` which returns a boolean.

### 2. `JSON.schema`, `JSON.validate`, `JSON.isvalid` No Longer Available
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this Claude? This is just wrong

**v1.x:**
```julia
using JSONSchema
JSON.isvalid(schema, data) # Worked via runtime registration
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. It was Base.isvalid, or just even isvalid

@odow
Copy link
Collaborator

odow commented Jan 14, 2026

I don't know how much of a review you want just yet. This needs a much more careful review before release.

My main ask is that we use Base.isvalid. That's the main use-case in JuMP-land: https://jump.dev/MathOptInterface.jl/stable/submodules/FileFormats/overview/#Validating-MOF-files

I'd also prefer that validate returned nothing if it was okay. What's the motivation for changing that?

@odow
Copy link
Collaborator

odow commented Jan 14, 2026

I have to admit, I don't use AI tools very much, and this experience doesn't make me want to. (I do use them to ask questions, etc, but not to write code.)

@quinnj
Copy link
Member Author

quinnj commented Jan 14, 2026

I think my main concern with Base.isvalid was whether it was too much of a "pun" (since it's used for string validity?) but I guess if that's what was being used before, it's probably fine to keep.

The reason for ValidationResult was so you could know: 1) is it valid and 2) if not, what were all the errors (instead of just throwing eagerly on the first encountered !isvalid).

We could probably come up with a different way to support that though and maintain the return nothing on valid request. Like, maybe we accumulate errors and return a ValidationError struct on non-valid and return nothing on valid.

Sorry for teh messiness; it's as much my fault here as the AI tool who tried its best. The issue is I originally had this code all in the JSON.jl package, but after a while, realized it should probably live as a stand-alone package. Then realized we already had a JSONSchema.jl package, and that's what led to the code moving here. And then I started moving the code a week or two ago and then tried to finish the move today and obviously messed up a few parts.

Sorry again, but thanks for the review! I'll get it cleaned up.

quinnj added a commit that referenced this pull request Jan 14, 2026
- Change validate() to return nothing on success (matches v1.x behavior)
- Extend Base.isvalid instead of exporting custom isvalid
- Restore copyright headers in src/schema.jl
- Remove main branch from CI.yml (only master exists)
- Fix URLs: JuliaServices -> JuliaIO in README.md and docs
- Fix migration.md to reflect actual v2.0 API behavior
- Update tests for new validate return type

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@odow
Copy link
Collaborator

odow commented Jan 14, 2026

What is the downside to this living in JSON.jl?

odow pushed a commit that referenced this pull request Jan 14, 2026
- Change validate() to return nothing on success (matches v1.x behavior)
- Extend Base.isvalid instead of exporting custom isvalid
- Restore copyright headers in src/schema.jl
- Remove main branch from CI.yml (only master exists)
- Fix URLs: JuliaServices -> JuliaIO in README.md and docs
- Fix migration.md to reflect actual v2.0 API behavior
- Update tests for new validate return type

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@odow odow deleted the jq/v2-rewrite branch January 14, 2026 20:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants